Skip to content

fix(render): disable browser scroll anchoring on the table holder - #4935

Draft
lukecotter wants to merge 4 commits into
tabulator-tables:masterfrom
lukecotter:fix/render-holder-overflow-anchor
Draft

fix(render): disable browser scroll anchoring on the table holder#4935
lukecotter wants to merge 4 commits into
tabulator-tables:masterfrom
lukecotter:fix/render-holder-overflow-anchor

Conversation

@lukecotter

@lukecotter lukecotter commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Problem

Chrome's scroll anchoring competes with the virtual renderer's padding writes. At the
bottom of a long list it pins scrollTop and the two fight in an infinite
oscillation — the renderer adjusts padding, anchoring restores the scroll offset, and
the cycle repeats every frame.

Fix

Set overflow-anchor: none on the table holder. The virtual renderer positions content
itself, so browser anchoring has nothing useful to contribute.

Test

test/e2e asserts the table holder disables scroll anchoring.

This PR also corrects the scroll-jump e2e measurement, because with anchoring off the
existing measurement no longer works and, once corrected, it reports a real defect on
current master. Two changes, in the last two commits:

  1. The grouped case stepped 250px in a 275px viewport. It tracks one row across each
    step, so that row must be in the DOM before and after. On master anchoring cancels most
    of every step — the test asks for 250px and gets 46, 14, 38, 54 — so the row survives by
    accident. With anchoring off the scroll does what it was asked, the whole window turns
    over, and no row is present in both states, so nothing is measured. The step is now
    80px, which keeps a trackable row.
  2. The jump was measured as moved − scrolled, where scrolled is the holder's own
    scrollTop delta. A virtual renderer revises its total scrollHeight as it measures
    real row heights, and scrollTop is read from the top of that changing document, so it
    shifts by the height correction while nothing on screen moves. It now compares the
    row's screen movement against the requested scroll distance, which has no such term.

Both tolerances, 20px and 5px, are unchanged. Verified against three other states: the new
metric still fails at 280px on the code before the #3654 fix, so it keeps that coverage; it
passes on this branch; and it fails at 124px on current master, where anchoring silently
cancels part of each requested scroll. That last figure is independent evidence for this
fix — the defect was previously invisible because the measurement absorbed it.

Performance

Neutral. 500k rows, K=5, medians:

Metric Before After
initial render (ms) 97.6 105.2
initial render, variable heights (ms) 105.6 109.0
fling churn, uniform 14205 14205
fling churn, variable 3935 3935

Chrome's scroll anchoring adjusts scrollTop when the virtual renderer inserts rows above the viewport, double-compensating against the renderer's own paddingTop/scrollTop management and causing drift on scroll-up. Set overflow-anchor:none on .tabulator-tableholder, the standard approach for JS-managed virtual scrollers.
e2e guard that .tabulator-tableholder computes overflow-anchor:none, so the
scroll-anchoring fix can't silently regress out of the built CSS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant